﻿using System;
using System.Net;
using Platformer_Example.Controller.Input;
using Platformer_Example.Controller.Network.Messages;
using Transformable_Engine_v2.Engine.Global;
using Transformable_Engine_v2.Engine.Network;
using Transformable_Engine_v2.Engine.Network.Interfaces;
using Transformable_Engine_v2.Engine.Network.Object;

namespace Platformer_Example.Controller.Network
{
    public class NetworkClient : BaseNetworkController
    {
        public const int Port = 32323;

        public NetworkClient()
        {
            Log.LogInfo("Client started and listening on port " + Port);
            NetworkController = new UdpNetworkClient(BaseNetworkClient.ClientType.Async);
            NetworkController.Receive(IPAddress.Any, Port);
            NetworkController.OnReceive += OnReceive;
        }

        private void OnReceive(IPEndPoint senderInfo, byte[] messageReceived)
        {
            var message = Serialization.Deserialize<NetworkSerializableObject>(messageReceived);
            switch (message.Id)
            {
                case SearchGameResponse.ID:
                    OnSearchGameResponseMessage(
                        Serialization.Deserialize<SearchGameResponse>(message.SerializableObject), senderInfo.Address);
                    break;
                case JoinGameDeclineResponse.ID:
                    OnJoinGameDeclineResponseMessage(
                        Serialization.Deserialize<JoinGameDeclineResponse>(message.SerializableObject),
                        senderInfo.Address);
                    break;
                case JoinGameAcceptResponse.ID:
                    OnJoinGameAcceptResponseMessage(
                        Serialization.Deserialize<JoinGameAcceptResponse>(message.SerializableObject),
                        senderInfo.Address);
                    break;
                case LoadGame.ID:
                    OnLoadGameMessage(Serialization.Deserialize<LoadGame>(message.SerializableObject),
                                      senderInfo.Address);
                    break;
                case PrepareForPlaying.ID:
                    OnPrepareForPlayingMessage(
                        Serialization.Deserialize<PrepareForPlaying>(message.SerializableObject), senderInfo.Address);
                    break;
                case InputPressed.ID:
                    OnInputPressedMessage(Serialization.Deserialize<InputPressed>(message.SerializableObject),
                                          senderInfo.Address);
                    break;
                case InputReleased.ID:
                    OnInputReleasedMessage(Serialization.Deserialize<InputReleased>(message.SerializableObject),
                                          senderInfo.Address);
                    break;
            }
        }

        public event OnMessageReceivedHandler<InputReleased> OnInputReleased;

        private void OnInputReleasedMessage(InputReleased message, IPAddress address)
        {
            Log.LogInfo("Received key " + message.Key + " pressed from player " + message.Id);
            if (OnInputReleased != null)
                OnInputReleased(message, address);
        }

        public event OnMessageReceivedHandler<InputPressed> OnInputPressed;

        private void OnInputPressedMessage(InputPressed message, IPAddress address)
        {
            Log.LogInfo("Received key " + message.Key + " pressed from player " + message.Id);
            if (OnInputPressed != null)
                OnInputPressed(message, address);
        }

        public event OnMessageReceivedHandler<PrepareForPlaying> OnPrepareForPlaying;

        private void OnPrepareForPlayingMessage(PrepareForPlaying message, IPAddress address)
        {
            Log.LogInfo("The server tells us the display the playing screen");
            if (OnPrepareForPlaying != null)
                OnPrepareForPlaying(message, address);
        }

        public event OnMessageReceivedHandler<LoadGame> OnLoadGame;

        private void OnLoadGameMessage(LoadGame message, IPAddress address)
        {
            Log.LogInfo("The server is telling us to start loading up the map");
            if (OnLoadGame != null)
                OnLoadGame(message, address);
        }

        public event OnMessageReceivedHandler<JoinGameAcceptResponse> OnJoinGameAcceptResponse;

        private void OnJoinGameAcceptResponseMessage(JoinGameAcceptResponse message, IPAddress address)
        {
            Log.LogInfo("Joining game...");
            if (OnJoinGameAcceptResponse != null)
                OnJoinGameAcceptResponse(message, address);
        }

        public event OnMessageReceivedHandler<JoinGameDeclineResponse> OnJoinGameDeclineResponse;

        private void OnJoinGameDeclineResponseMessage(JoinGameDeclineResponse message, IPAddress address)
        {
            Log.LogInfo("Joining game declined");
            if (OnJoinGameDeclineResponse != null)
                OnJoinGameDeclineResponse(message, address);
        }

        public event OnMessageReceivedHandler<SearchGameResponse> OnSearchGameResponse;

        private void OnSearchGameResponseMessage(SearchGameResponse message, IPAddress address)
        {
            Log.LogInfo("Received reply from a visible server");
            if (OnSearchGameResponse != null)
                OnSearchGameResponse(message, address);
        }

        public void TryJoiningServer(IPAddress serverAddress, string playerName)
        {
            Log.LogInfo("Trying to join a server with address " + serverAddress + " as " + playerName);
            NetworkController.Send(serverAddress, NetworkServer.Port, new JoinGame
                                                                          {
                                                                              PlayerName = playerName,
                                                                          });
        }

        public void SearchGames()
        {
            Log.LogInfo("Searching games in LAN");
            NetworkController.Send(IPAddress.Broadcast, NetworkServer.Port, new SearchGame());
        }

        public void SendLoadingDone(IPAddress serverIp, string clientId)
        {
            Log.LogInfo("Done loading...");
            NetworkController.Send(serverIp, NetworkServer.Port, new LoadingDone
                                                                     {
                                                                         Client = clientId
                                                                     });
        }

        public void SendInputPressed(IPAddress serverIp, Guid id, InputState.Key key)
        {
            Log.LogInfo("Sending local input pressed " + key + " to server");
            NetworkController.Send(serverIp, NetworkServer.Port, new InputPressed
                                                                     {
                                                                         Key = key,
                                                                         PlayerId = id.ToString()
                                                                     });
        }

        public void SendInputReleased(IPAddress serverIp, Guid id, InputState.Key key)
        {
            Log.LogInfo("Sending local input released " + key + " to server");
            NetworkController.Send(serverIp, NetworkServer.Port, new InputReleased
                                                                     {
                                                                         Key = key,
                                                                         PlayerId = id.ToString()
                                                                     });
        }
    }
}